package com.caiyi.financial.nirvana.sms.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.caiyi.financial.nirvana.sms.entity.SmsAdmin;
import com.caiyi.financial.nirvana.sms.entity.SmsPermission;
import com.caiyi.financial.nirvana.sms.entity.SmsRole;
import com.caiyi.financial.nirvana.sms.entity.SmsUser;
import com.caiyi.financial.nirvana.sms.enums.ReturnCodeEnum;
import com.caiyi.financial.nirvana.sms.jedis.JedisKey;
import com.caiyi.financial.nirvana.sms.jedis.JedisService;
import com.caiyi.financial.nirvana.sms.querybean.AdminQueryBean;
import com.caiyi.financial.nirvana.sms.querybean.MenuBean;
import com.caiyi.financial.nirvana.sms.service.SmsAdminService;
import com.caiyi.financial.nirvana.sms.service.SmsPermissionService;
import com.caiyi.financial.nirvana.sms.service.SmsRoleService;
import com.caiyi.financial.nirvana.sms.service.SmsUserService;
import com.caiyi.financial.nirvana.sms.shiro.SmsShiroFilterFactoryBean;
import com.caiyi.financial.nirvana.sms.util.*;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.youyu.common.api.Result;
import com.youyu.common.utils.YyAssert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import tk.mybatis.mapper.entity.Example;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Mario on 2018/1/4 0004.
 * 后台管理用户
 */
@Api(description = "后台管理用户接口")
@RequestMapping("sms/admin")
@RestController
public class AdminController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private static final String LOGIN_ACCOUNT = "login";

    private static final String SMS_MSG_CAPTCHA_KEY = "SMS_MSG_CAPTCHA";
    private static final String IS_CAPTCHA_VALID = "IS_CAPTCHA_VALID";

    @Autowired
    private SmsPermissionService smsPermissionService;

    @Autowired
    private SmsRoleService roleService;

    @Autowired
    private SmsAdminService adminService;

    @Autowired
    private SmsShiroFilterFactoryBean smsShiroFilterFactoryBean;

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Autowired
    private SmsUserService smsUserService;

    @Value("${spring.profiles.active}")
    private String env;

    @ApiOperation(value = "登录信息", notes = "登录详细描述")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "account", value = "用户名", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "password", value = "用户密码", required = true, dataType = "String", paramType = "query")
    })
    @PostMapping(value = "/login")
    public Result login(@RequestParam String account, @RequestParam String password, HttpSession session, HttpServletRequest request) {
        Result result = new Result();
        result.setCode(ReturnCodeEnum.FAILED.code);
        //获取IP
        String ip = CommUtil.getClientIp(request);
        String ipKey = String.format(JedisKey.SMS_COMMON_IP, ip);
        JedisService.expire(ipKey, 60);
        long loginNum = JedisService.incr(ipKey);
        YyAssert.isFalse(isExceedLoginNum(loginNum), ReturnCodeEnum.FAILED.code, "请一分钟后再登录！");
        SmsAdmin user = adminService.selectByAccount(account);
        if (user == null) {
            //账户不存在
            result.setDesc("账号不存在");
            return result;
        }
        if (!user.getPassword().equals(MD5Util.encode(password)) && !user.getPassword().equals(password)) {
            //密码错误
            result.setDesc("密码错误");
            return result;
        }
        // 登录信息添加到会话
        UsernamePasswordToken usernamePasswordToken;
        if (user.getPassword().equals(password)) {
            usernamePasswordToken = new UsernamePasswordToken(account, password);
        } else {
            usernamePasswordToken = new UsernamePasswordToken(account, MD5Util.encode(password));
        }
        Subject subject = SecurityUtils.getSubject();
        //保存登录信息
        subject.login(usernamePasswordToken);
        JSONObject loginData = this.createUserInfo(user);
        //登录成功
        JedisService.del(ipKey);
        return Result.ok(loginData);
    }

    private boolean isExceedLoginNum(long loginNum) {
        return loginNum > Constant.LOGIN_NUM;
    }

    @ApiOperation("用户信息接口")
    @PostMapping(value = "/info")
    public Result info() {
        Subject subject = SecurityUtils.getSubject();
        SmsAdmin user = (SmsAdmin) subject.getPrincipal();
        if (user == null) {
            // 用户未登录
            return Result.fail(ReturnCodeEnum.NOT_LOGIN_ERROR);
        }
        JSONObject loginData = this.createUserInfo(user);
        return Result.ok(loginData);
    }

    @ApiOperation("刷新权限数据")
    @PostMapping(value = "/refreshAuth")
    public Result refreshAuthData() {
        smsShiroFilterFactoryBean.refreshAuth();
        return new Result();
    }

    @ApiOperation("注销")
    @PostMapping(value = "/logout")
    public Result logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return new Result();
    }

    @ApiOperation("查询admin用户")
    @PostMapping(value = "/getUsers")
    public Result getUsers(@RequestBody AdminQueryBean bean) {
        return Result.ok(adminService.getAdminByParams(bean));
    }

    @ApiOperation("添加Admin用户")
    @PostMapping(value = "/addUser")
    public Result addUser(@RequestBody SmsAdmin smsAdmin) {
        // 密码MD5加密存储
        YyAssert.isFalse(isAccountNameAlreadyExist(smsAdmin), ReturnCodeEnum.FAILED.code, "账户名已存在");
        if (adminService.insertByEntitySelective(smsAdmin) == 1) {
            return Result.ok();
        }
        return Result.fail(ReturnCodeEnum.FAILED);
    }

    private boolean isAccountNameAlreadyExist(SmsAdmin smsAdmin) {
        smsAdmin.setPassword(MD5Util.encode(smsAdmin.getPassword()));
        Example example = new Example(SmsAdmin.class);
        example.createCriteria().andEqualTo("account", smsAdmin.getAccount());
        return this.adminService.queryListByExample(example).size() > 0;
    }

    @ApiOperation("编辑Admin用户")
    @PostMapping(value = "/modUser")
    public Result editUser(@RequestBody SmsAdmin smsAdmin) {
        if (StringUtils.isNotBlank(smsAdmin.getPassword())) {
            smsAdmin.setPassword(MD5Util.encode(smsAdmin.getPassword()));
        }
        if (adminService.updateByEntitySelective(smsAdmin) != 1) {
            return Result.fail(ReturnCodeEnum.FAILED);
        }
        return new Result();
    }

    @ApiOperation("修改密码")
    @PostMapping(value = "/modPwd")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "oldPwd", value = "旧密码", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "newPwd", value = "新密码", required = true, dataType = "String", paramType = "query")
    })
    public Result modPwd(@RequestParam String oldPwd, @RequestParam String newPwd) {
        Subject subject = SecurityUtils.getSubject();
        SmsAdmin user = (SmsAdmin) subject.getPrincipal();
        Result result = new Result();
        // 用户未登录
        YyAssert.isFalse(user == null, ReturnCodeEnum.NOT_LOGIN_ERROR);
        if (user.getPassword().equals(MD5Util.encode(oldPwd))) {
            user.setPassword(MD5Util.encode(newPwd));
            int res = adminService.updateByPrimaryKey(user);
            if (res == 1) {
                result.setDesc("修改成功");
                return result;
            }
            result.setCode(ReturnCodeEnum.FAILED.code);
            result.setDesc("修改失败");
        }
        result.setCode(ReturnCodeEnum.FAILED.code);
        result.setDesc("旧密码错误");
        return result;

    }

    @ApiOperation("删除Admin用户")
    @PostMapping(value = "/delUser")
    public Result delUser(@RequestParam Integer id) {
        SmsAdmin smsAdmin = new SmsAdmin();
        smsAdmin.setId(id);
        if (adminService.deleteByPrimaryKey(smsAdmin) != 1) {
            return Result.fail(ReturnCodeEnum.FAILED);
        }
        return Result.ok();
    }

    @ApiOperation("图片验证码")
    @GetMapping("/captcha")
    public void getCaptcha(HttpSession session, HttpServletResponse response) throws IOException {
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");
        String capText = captchaProducer.createText();
        //存入Session
        session.setAttribute(captchaProducer.getConfig().getSessionKey(), capText);
        session.setAttribute(IS_CAPTCHA_VALID, true);
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }

    @ApiOperation("发送短信")
    @PostMapping("/sendCodeMessage")
    @ApiImplicitParam(name = "captcha", value = "验证码", required = true, dataType = "String", paramType = "query")
    public Result sendCodeMessage(String captcha, HttpSession session) {
        Result result = new Result();
        //验证码过期检测
        if (session.getAttribute(IS_CAPTCHA_VALID) != null &&
                !(boolean) (session.getAttribute(IS_CAPTCHA_VALID))) {
            result.setCode(ReturnCodeEnum.CAPTCHA_INVALID.code);
            result.setDesc(ReturnCodeEnum.CAPTCHA_INVALID.desc);
            return result;
        }
        String savedCaptcha = session.getAttribute(captchaProducer.getConfig().getSessionKey()) == null ?
                null : session.getAttribute(captchaProducer.getConfig().getSessionKey()).toString();
        String account = session.getAttribute("account") == null ? null : session.getAttribute("account").toString();
        if (savedCaptcha == null || account == null) {
            result.setCode(ReturnCodeEnum.NOT_LOGIN_ERROR.code);
            result.setDesc(ReturnCodeEnum.NOT_LOGIN_ERROR.desc);
            return result;
        }
        //校验图片验证码
        if (savedCaptcha.equals(captcha)) {
            //验证码过期 存入Session
            session.setAttribute(IS_CAPTCHA_VALID, false);
            SmsAdmin user = adminService.selectByAccount(account);
            if (!StringUtils.isBlank(user.getPhone())) {
                //发送短信
                String code = (int) ((Math.random() * 9 + 1) * 100000) + "";
                String sendUrl = CommUtil.getSendUrl(env);
                Map<String, String> params = new HashMap<>();
                params.put("account", LOGIN_ACCOUNT);
                params.put("content", String.format(Constant.SMS_LOGIN_CONTENT, code));
                params.put("phones", user.getPhone());
                SmsUser apiUser = smsUserService.selectByAccount(LOGIN_ACCOUNT);
                if (apiUser == null) {
                    result.setDesc("短信账户" + LOGIN_ACCOUNT + "为空。");
                    result.setCode(ReturnCodeEnum.FAILED.getCode());
                } else {
                    String sign = SignUtil.createSign(params, apiUser.getPassword());
                    params.put("sign", sign);
                    String content = HttpClientUtil.doPost(sendUrl, JSON.toJSONString(params));
                    logger.info("登录短信发送结果:" + content);
                    session.setAttribute(SMS_MSG_CAPTCHA_KEY, code);
                    result.setDesc("发送成功");
                }
            } else {
                result.setDesc("账号未添加手机号码,请联系管理员.");
                result.setCode(ReturnCodeEnum.FAILED.getCode());
            }
        } else {
            result.setDesc("图形验证码错误");
            result.setCode(ReturnCodeEnum.FAILED.getCode());
        }
        return result;
    }

    @ApiOperation("提交短信验证码")
    @PostMapping("/submitMessageCode")
    @ApiImplicitParam(name = "msgCode", value = "短信验证码", required = true, dataType = "String", paramType = "query")
    public Result submitMessageCode(String msgCode, HttpSession session) {
        String savedMsgCode = session.getAttribute(SMS_MSG_CAPTCHA_KEY) == null ? null : session.getAttribute(SMS_MSG_CAPTCHA_KEY).toString();
        Result result = new Result();
        if (savedMsgCode == null) {
            result.setCode(ReturnCodeEnum.NOT_LOGIN_ERROR.code);
            result.setDesc(ReturnCodeEnum.NOT_LOGIN_ERROR.desc);
            return result;
        }
        if (savedMsgCode.equals(msgCode)) {
            String account = session.getAttribute("account").toString();
            String password = session.getAttribute("password").toString();
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(account, MD5Util.encode(password));
            Subject subject = SecurityUtils.getSubject();
            //保存登录信息
            subject.login(usernamePasswordToken);
            session.removeAttribute("account");
            session.removeAttribute("password");
            session.removeAttribute(SMS_MSG_CAPTCHA_KEY);
            session.removeAttribute(captchaProducer.getConfig().getSessionKey());
            //添加用户信息
            SmsAdmin user = adminService.selectByAccount(account);
            JSONObject loginData = this.createUserInfo(user);
            result.setData(loginData);
        } else {
            result.setDesc("短信验证码错误");
            result.setCode(ReturnCodeEnum.FAILED.getCode());
        }
        return result;
    }

    /**
     * 用户基本信息
     *
     * @param user user
     * @return info
     */
    private JSONObject createUserInfo(SmsAdmin user) {
        //查询角色
        SmsRole role = new SmsRole();
        role.setId(user.getRoleId());
        role = roleService.selectByPrimaryKey(role);
        //根据角色查询权限
        List<SmsPermission> authPermissions = this.smsPermissionService.queryAllPermissionByRole(role);
        logger.info("user id : {}, authPermissions : {}", user.getId(), authPermissions);
        //将权限整合为菜单树
        List<MenuBean> menuBeanList = this.smsPermissionService.formatMenus(authPermissions);
        logger.info("user id : {}, menuBeanList : {}", user.getId(), menuBeanList);
        JSONObject userInfo = new JSONObject();
        userInfo.put("user", user);
        userInfo.put("menu", menuBeanList);
        return userInfo;
    }
}
